package edu.whut.framework.inject;

import com.sun.org.apache.bcel.internal.util.ClassSet;
import edu.whut.framework.annotation.Autowired;
import edu.whut.framework.container.BeanContainer;
import edu.whut.framework.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @program: MySpring
 * @description: 依赖注入实现类
 * @author: Wayne
 * @create: 2020-05-25 20:54
 **/
@Slf4j
public class DependencyInjector {

    private BeanContainer beanContainer;

    public DependencyInjector(){
        beanContainer = BeanContainer.getInstance();
    }

    public void doAutowired(){
        //1.遍历所有Class对象
        if (beanContainer.getClasses() == null){
            log.warn("IOC容器为空");
            return;
        }

        for(Class<?> clazz : beanContainer.getClasses()) {
            //获取所有的成员变量 然后看它头上有没有Autowired注解
            Field[] fields = clazz.getDeclaredFields();
            Set<Field> fieldSet = null;
            if (fields == null || fields.length == 0){
                continue;
            }
            //通过反射注入实例变量
            Object targetInstance = beanContainer.getBean(clazz);
            fieldSet = new HashSet<>();
            for (Field field : fields) {
                fieldSet.add(field);
                setFieldInstanceByFieldInject(field, targetInstance);
            }
            setFiledInstanceByMethodInject(clazz, fieldSet, targetInstance);
        }
    }

    /**
     * 根据类型在容器中获得实例
     * @param type
     * @return
     */
    private Object getFieldInstance(Class<?> type, String name) {
        Object fieldInstance = beanContainer.getBean(type);
        if (fieldInstance != null) {
            return fieldInstance;
        }
        Class<?> fieldClass = getServiceImplClass(type, name);
        if (fieldClass ==null){
            return null;
        }
        return beanContainer.getBean(fieldClass);

    }

    /**
     * 根据接口类在IOC容器类获得实现类
     * @param fieldClazz
     * @param name
     * @return
     */
    private Class<?> getServiceImplClass(Class<?> fieldClazz, String name){
        Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldClazz);
        if (classSet != null && classSet.size() > 0){
            if (name ==null || "".equals(name)){
                if (classSet.size() ==1 ){
                    return classSet.iterator().next();
                } else {
                    throw new RuntimeException("有多个实现类,没有指定实现类的名字");
                }
            } else {
                for (Class<?> clazz : classSet) {
                    if (name.equals(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 通过set方法来注入field实例
     * @param targetClass
     */
    private void setFiledInstanceByMethodInject(Class<?> targetClass, Set<Field> fieldSet, Object targetInstance) {
        Method[] methods = targetClass.getDeclaredMethods();
        if (methods == null || methods.length == 0) {
            return;
        }
        for (Method method : methods) {
            //如何方法上面有Autowired注解
            if (method.isAnnotationPresent(Autowired.class)) {
                String name = method.getAnnotation(Autowired.class).name();
                Parameter[] parameters = method.getParameters();
                if (parameters == null || parameters.length == 0) {
                    throw new RuntimeException("使用Autowired但没有指定参数注入");
                }
                for (Parameter parameter : parameters) {
                    Class<?> parameterClass = parameter.getType();
                    Object fieldInstance = getFieldInstance(parameterClass, name);
                    Iterator<Field> iterator = fieldSet.iterator();
                    while (iterator.hasNext()) {
                        Field field = iterator.next();
                        if (field.getType().equals(parameterClass)) {
                            ClassUtil.setField(field, targetInstance, fieldInstance);
                        }
                    }

                }
            }
        }
    }

    /**
     * 根据字段上的Autowired来注入
     * @param field
     * @param targetInstance
     */
    private void setFieldInstanceByFieldInject(Field field, Object targetInstance){
        if (field.isAnnotationPresent(Autowired.class)) {
            //如果存在 获取类型
            Class<?> type = field.getType();
            //获取注解上指定的名字
            Autowired autowired = field.getAnnotation(Autowired.class);
            String name = autowired.name();
            //在容器里面寻找bean实例
            Object fieldInstance = getFieldInstance(type, name);
            if (fieldInstance == null) {
                throw new RuntimeException("实例不存在，不能注入");
            } else {
                ClassUtil.setField(field, targetInstance, fieldInstance);
            }
        }
    }
}
